package com.firexxx.detect.monitor;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.openjdk.jol.info.ClassData;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.FieldLayout;
import org.openjdk.jol.util.ClassUtils;
import org.openjdk.jol.vm.VM;
import org.openjdk.jol.vm.VirtualMachine;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * TODO
 *
 * @author fireXXX
 * @date 2020/11/14 23:12
 **/
public class MonitorTest {

    private static String toHex(int x) {
        String s = Integer.toHexString(x);
        int deficit = 2 - s.length();
        for (int c = 0; c < deficit; c++) {
            s = "0" + s;
        }
        return s;
    }

    private static String toBinary(int x) {
        String s = Integer.toBinaryString(x);
        int deficit = 8 - s.length();
        for (int c = 0; c < deficit; c++) {
            s = "0" + s;
        }
        return s;
    }

    public static String toPrintable(JavaObjectHead instance) throws JsonProcessingException {
        ClassLayout classLayout = ClassLayout.parseInstance(instance);
        ClassData classData = ClassData.parseInstance(instance);

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        String MSG_GAP = "(alignment/padding gap)";
        String MSG_NEXT_GAP = "(loss due to the next object alignment)";

        int maxTypeLen = "TYPE".length();
        for (FieldLayout f : classLayout.fields()) {
            maxTypeLen = Math.max(f.typeClass().length(), maxTypeLen);
        }
        maxTypeLen += 2;

        int maxDescrLen = Math.max(MSG_GAP.length(), MSG_NEXT_GAP.length());
        for (FieldLayout f : classLayout.fields()) {
            maxDescrLen = Math.max(f.shortFieldName().length(), maxDescrLen);
        }
        maxDescrLen += 2;

        if (instance != null) {
            try {
                Class<?> klass = ClassUtils.loadClass(classData.name());
                if (!klass.isAssignableFrom(instance.getClass())) {
                    throw new IllegalArgumentException("Passed instance type " + instance.getClass() + " is not assignable from " + klass + ".");
                }
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("Class is not found: " + classData.name() + ".");
            }
        }

        pw.println(classData.name() + " object internals:");
        pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", "OFFSET", "SIZE", "TYPE", "DESCRIPTION", "VALUE");
        if (instance != null) {
            VirtualMachine vm = VM.current();

            for (long off = 0; off < classLayout.headerSize(); off += 4) {
                int word = vm.getInt(instance, off);
                pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)",
                        toHex((word >> 0)  & 0xFF) + " " +
                                toHex((word >> 8)  & 0xFF) + " " +
                                toHex((word >> 16) & 0xFF) + " " +
                                toHex((word >> 24) & 0xFF) + " " +
                                "(" +
                                toBinary((word >> 0)  & 0xFF) + " " +
                                toBinary((word >> 8)  & 0xFF) + " " +
                                toBinary((word >> 16) & 0xFF) + " " +
                                toBinary((word >> 24) & 0xFF) + ") " +
                                "(" + word + ")"
                );
            }
        } else {
            pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", 0, classLayout.headerSize(), "", "(object header)", "N/A");
        }

        long nextFree = classLayout.headerSize();

        long interLoss = 0;
        long exterLoss = 0;

        long sizeOf = (instance != null) ? VM.current().sizeOf(instance) : classLayout.instanceSize();

        if (sizeOf != nextFree) {
            exterLoss = sizeOf - nextFree;
            pw.printf(" %6d %5s %" + maxTypeLen + "s %s%n", nextFree, exterLoss, "", MSG_NEXT_GAP);
        }

        pw.printf("Instance size: %d bytes%n", sizeOf);
        pw.printf("Space losses: %d bytes internal + %d bytes external = %d bytes total%n", interLoss, exterLoss, interLoss + exterLoss);

        pw.close();

        return sw.toString();
    }

    public static void main(String[] args) throws JsonProcessingException {
        Object head = new Object();

        System.out.println(JavaHeadParser.parse(head).toJson());
        synchronized (head) {
            System.out.println("加锁");
            System.out.println(JavaHeadParser.parse(head).toJson());
        }

        System.out.println(JavaHeadParser.parse(head).toJson());
    }

}